// Copyright (2017) Baidu Inc. All rights reserveed.
/**
 * File: baidu_alarm.cpp
 * Auth: Gang Chen (chengang12@baidu.com)
 * Desc: Light duer alarm procedure.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "baidu_alarm.h"
#include "duer_log.h"
#include "baidu_json.h"
#include "baidu_ca.h"
#include "baidu_ca_scheduler.h"
#include "heap_monitor.h"

namespace duer {

static duer_set_alarm_func s_set_alarm_cb = NULL;
static duer_delete_alarm_func s_delete_alarm_cb = NULL;

int duer_report_alarm_event(int id, const char *token, alarm_event_type type)
{
    baidu_json *event = NULL;
    baidu_json *device_event = NULL;
    baidu_json *header = NULL;
    baidu_json *payload = NULL;
    baidu_json *data = NULL;
    char *name = NULL;
    char *data_name = NULL;
    int rs = BCA_NO_ERR;
    char* msg_id = "YWkuZHVlcm9zLmJvdC5hbGFybSthbG11VydHNfNThiMDQyM11DFmMzFiOQ==";

    data = baidu_json_CreateObject();
    event = baidu_json_CreateObject();
    device_event = baidu_json_CreateObject();
    header = baidu_json_CreateObject();
    payload = baidu_json_CreateObject();
    if (!data || !event || !device_event || !header || !payload) {
        rs = BCA_ERR_INTERNAL;
        goto error_out;
    }

    switch (type) {
    case SET_ALERT_SUCCESS:
        name = "SetAlertSucceeded";
        data_name = "set_alert_succeeded";
        break;
    case SET_ALERT_FAIL:
        name = "SetAlertFailed";
        data_name = "set_alert_failed";
        break;
    case DELETE_ALERT_SUCCESS:
        name = "DeleteAlertSucceeded";
        data_name = "delete_alert_succeeded";
        break;
    case DELETE_ALERT_FAIL:
        name = "DeleteAlertFailed";
        data_name = "delete_alert_failed";
        break;
    case ALERT_START:
        name = "AlertStarted";
        data_name = "alert_started";
        break;
    case ALERT_STOP:
        name = "AlertStopped";
        data_name = "alert_stopped";
        break;
    default:
        rs = BCA_ERR_INTERNAL;
        goto error_out;
    }

    if (id) {
        baidu_json_AddNumberToObject(event, "id", id);
    }

    baidu_json_AddStringToObject(header, "namespace", "Alerts");
    baidu_json_AddStringToObject(header, "name", name);
    baidu_json_AddStringToObject(header, "message_id", msg_id);
    baidu_json_AddItemToObject(device_event, "header", header);
    baidu_json_AddStringToObject(payload, "token", token);
    baidu_json_AddItemToObject(device_event, "payload", payload);
    baidu_json_AddItemToObject(event, "device_event", device_event);
    baidu_json_AddItemToObject(data, data_name, event);
    Scheduler::instance().report(data);
    baidu_json_Delete(data);

    return rs;

error_out:
    if (data) {
        baidu_json_release(data);
    }

    if (event) {
        baidu_json_release(event);
    }

    if (device_event) {
        baidu_json_release(device_event);
    }

    if (header) {
        baidu_json_release(header);
    }

    if (payload) {
        baidu_json_release(payload);
    }

    return rs;
}

static bca_status_t duer_parse_directive(bca_context ctx,
        bca_msg_t* msg,
        bca_addr_t* addr,
        baidu_json **directive)
{
    int ret = BCA_NO_ERR;
    char *action = NULL;
    baidu_json *value = NULL;

    if (!ctx || !msg) {
        ret = BCA_MSG_RSP_BAD_REQUEST;
        goto RET;
    }

    if (!msg->payload || msg->payload_len <= 0) {
        ret = BCA_MSG_RSP_BAD_REQUEST;
        goto RET;
    }

    action = (char *)MALLOC(msg->payload_len + 1, ALARM);
    if (!action) {
        ret = BCA_ERR_INTERNAL;
        DUER_LOGE("Memory is not enough");
        goto RET;
    }
    memcpy(action, msg->payload, msg->payload_len);
    action[msg->payload_len] = '\0';

    value = baidu_json_Parse(action);
    if (!value) {
        ret = BCA_MSG_RSP_BAD_REQUEST;
        DUER_LOGE("Failed to parse json");
        goto RET;
    }

    *directive = value;

RET:
    if ((ret != BCA_NO_ERR) && value) {
        baidu_json_Delete(value);
    }

    if (action) {
        FREE(action);
    }

    return ret;
}

static bca_status_t duer_set_alarm(bca_context ctx, bca_msg_t* msg, bca_addr_t* addr)
{
    int ret = BCA_NO_ERR;
    int res = BCA_NO_ERR;
    int time = 0;
    baidu_json *directive = NULL;
    baidu_json *payload = NULL;
    baidu_json *scheduled_time = NULL;
    baidu_json *token = NULL;
    baidu_json *id = NULL;
    baidu_json *url = NULL;

    ret = duer_parse_directive(ctx, msg, addr, &directive);
    if (ret != BCA_NO_ERR) {
        goto RET;
    }

    payload = baidu_json_GetObjectItem(directive, "payload");
    if (!payload) {
        ret = BCA_MSG_RSP_BAD_REQUEST;
        DUER_LOGE("Failed to parse json");
        goto RET;
    }

    token = baidu_json_GetObjectItem(payload, "token");
    scheduled_time = baidu_json_GetObjectItem(payload, "scheduled_time");
    id = baidu_json_GetObjectItem(directive, "id");
    url = baidu_json_GetObjectItem(directive, "url");

    if (!token || !scheduled_time || !id || !url) {
        ret = BCA_MSG_RSP_BAD_REQUEST;
        DUER_LOGE("Failed to parse json");
        goto RET;
    }

    DUER_LOGD("id: %d", id->valueint);
    DUER_LOGD("token:%s", token->valuestring);
    DUER_LOGD("scheduled_time:%s", scheduled_time->valuestring);
    DUER_LOGD("url:%s", url->valuestring);

    res = sscanf(scheduled_time->valuestring, "%d", &time);
    if (res != 1) {
        ret = BCA_MSG_RSP_BAD_REQUEST;
        DUER_LOGE("Failed to get schedule time");
        goto RET;
    }

    if (s_set_alarm_cb) {
        s_set_alarm_cb(id->valueint, token->valuestring, url->valuestring, time);
    }

RET:
    if (directive) {
        baidu_json_Delete(directive);
    }

    return ret;
}

static bca_status_t duer_delete_alarm(bca_context ctx, bca_msg_t* msg, bca_addr_t* addr)
{
    baidu_json *directive = NULL;
    baidu_json *payload = NULL;
    baidu_json *token = NULL;
    baidu_json *id = NULL;
    int ret = BCA_NO_ERR;

    ret = duer_parse_directive(ctx, msg, addr, &directive);
    if (ret != BCA_NO_ERR) {
        goto RET;
    }

    payload = baidu_json_GetObjectItem(directive, "payload");
    if (!payload) {
        ret = BCA_MSG_RSP_BAD_REQUEST;
        DUER_LOGE("Failed to parse json");
        goto RET;
    }

    token = baidu_json_GetObjectItem(payload, "token");
    id = baidu_json_GetObjectItem(directive, "id");
    if (!token || !id) {
        ret = BCA_MSG_RSP_BAD_REQUEST;
        DUER_LOGE("Failed to parse json");
        goto RET;
    }

    DUER_LOGD("id: %d, token:%s", id->valueint, token->valuestring);

    if (s_delete_alarm_cb) {
        s_delete_alarm_cb(id->valueint, token->valuestring);
    }

RET:
    if (directive) {
        baidu_json_Delete(directive);
    }

    return ret;
}

void duer_alarm_initialize(duer_set_alarm_func set_alarm,
                           duer_delete_alarm_func delete_alarm)
{
    s_set_alarm_cb = set_alarm;
    s_delete_alarm_cb = delete_alarm;

    bca_res_t res[] = {
        {BCA_RES_MODE_DYNAMIC, BCA_RES_OP_PUT, "set_alarm", duer_set_alarm},
        {BCA_RES_MODE_DYNAMIC, BCA_RES_OP_PUT, "delete_alarm", duer_delete_alarm},
    };
    duer::Scheduler::instance().add_controll_points(res, sizeof(res) / sizeof(res[0]));
}

}

